package com.hugang.security;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 解决线程安全问题方式三：Lock锁 --- JDK5.0新增
 * 面试题：synchronized和Lock的异同？
 * 相同点：都可以用来解决线程安全问题
 * 不同点：
 *  1）、原始构成
 *      synchronized是关键字，属于JVM层面
 *          monitorenter(底层是通过monitor对象来完成，其实wait/notify等方法也依赖于monitor对象，只有在同步块或同步方法中才能调用wait/notify等方法)
 *          monitorexit
 *      ReentrantLock是juc下的具体的类，是api层面的锁。
 *  2）、使用方法
 *      synchronized不需要用户去手动释放锁，当synchronized代码执行完成之后系统会自动让线程释放对锁的占用
 *      ReentrantLock则需要用户去手动释放锁，若没有主动释放锁，就有可能导致死锁现象。需要lock()、unlock()方法配合try finally语句块来完成。
 *  3）、是否可以中断
 *      synchronized不可中断，除非抛出异常或者正常运行完成。
 *      ReentrantLock可中断：
 *      1）、设置超时方法 tryLock(long timeout, TimeUnit unit)
 *      2）、使用lockInterruptibly()替换lock()，调用此线程的interrupt()方法可中断。需要处理InterruptedException
 *  4）、加锁是否公平
 *      synchronized非公平锁
 *      ReentrantLock默认为公平锁，可通过构造方法指定，true为公平锁，false为非公平锁
 *  5）、绑定多个条件Condition
 *      synchronized没有
 *      ReentrantLock可以通过Condition用来实现分组唤醒需要唤醒的线程们，可以精确唤醒(java_juc/../communication/TestOrderAccess.java)，而不是像synchronized要么唤醒全部线程，要么随机唤醒一个线程
 * 优先使用顺序：Lock -> 同步代码块 -> 同步方法
 *
 * @author hg
 * @date 2020/6/15 19:02
 */
public class LockTest {

    public static void main(String[] args) throws InterruptedException {
        Window window = new Window();
        Thread t1 = new Thread(window);
        Thread t2 = new Thread(window);
        Thread t3 = new Thread(window);
//        Thread t1 = new Window1();
//        Thread t2 = new Window1();
//        Thread t3 = new Window1();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();

        TimeUnit.SECONDS.sleep(1);
        t1.interrupt();
    }

}

class Window implements Runnable {
    //1、实例化ReentrantLock对象
    private  ReentrantLock lock = new ReentrantLock(false);
    private int ticket = 100;

    @Override
    public void run() {
        while (true) {
            //2、调用锁定方法lock()
//            lock.lock();
            try {
                lock.lockInterruptibly();
//                Thread.currentThread().interrupt();
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println(Thread.currentThread().getName() +" ---> 被打断");
            }
            try {
                if (ticket > 0) {
                    try {
                        Thread.sleep(20);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + ":卖票，票号为：" + ticket--);
                } else {
                    break;
                }
            } finally {
                //3、调用解锁方法unlock()
                lock.unlock();
            }

        }
    }
}

class Window1 extends Thread {
    //使用继承Thread类的方式需要定义静态的Lock对象
    private static ReentrantLock lock = new ReentrantLock();
    private static int ticket = 100;

    @Override
    public void run() {

        while (true) {
            //2、调用锁定方法lock()
            lock.lock();
            try {
                if (ticket > 0) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":卖票，票号为：" + ticket--);
                } else {
                    break;
                }
            } finally {
                //3、调用解锁方法unlock()
                lock.unlock();
            }

        }
    }
}
